package pro.cherish.knowledgeormmysql.config.mybatis;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.parsers.DynamicTableNameParser;
import com.baomidou.mybatisplus.extension.parsers.ITableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.statement.delete.Delete;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.*;

@Configuration
@Slf4j
@EnableTransactionManagement
@MapperScan("pro.cherish.knowledgeormmysql.mapper")
public class MybatisPlusConfig {

    /**
     * 分页插件
     * @return
     */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        /**
         * fixme 在此处使用mybatis plus 官方文档中所说的，使用 【攻击 SQL 阻断解析器】作用！阻止恶意的全表更新删除
         */
        List<ISqlParser> sqlParserList = new ArrayList<>();
        BlockAttackSqlParser blockAttackSqlParser = createBlockAttackSqlParser();
        sqlParserList.add(blockAttackSqlParser);
        /**
         * fixme 在此处使用mybatis plus 官方文档中所说的，使用 【攻击 SQL 阻断解析器】作用！阻止恶意的全表更新删除
         */
        /**
         * fixme 添加动态表名解析器
         */
        DynamicTableNameParser dynamicTableNameParser = createDynamicTableNameParser();
        sqlParserList.add(dynamicTableNameParser);
        /**
         * fixme 添加动态表名解析器
         */
        /**
         * fixme 添加多租户解析
         */
        TenantSqlParser tenantSqlParser = createTenantSqlParser();
        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        /**
         * fixme 添加多租户解析
         */


        return paginationInterceptor;
    }

    /**
     * 插件阻断拦截器
     * @return
     */
    public BlockAttackSqlParser createBlockAttackSqlParser(){
        // 攻击 SQL 阻断解析器、加入解析链
        BlockAttackSqlParser blockAttackSqlParser = new BlockAttackSqlParser() {
            @Override
            public void processDelete(Delete delete) {
                // 如果你想自定义做点什么，可以重写父类方法像这样子
                if ("user".equals(delete.getTable().getName())) {
                    // 自定义跳过某个表，其他关联表可以调用 delete.getTables() 判断
                    return;
                }
                super.processDelete(delete);
            }
        };
        return blockAttackSqlParser;
    }

    /**
     * 动态表名解析器
     * @return
     */
    public DynamicTableNameParser createDynamicTableNameParser(){
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
            put("tableName", (metaObject, sql, tableName) -> {
                // metaObject 可以获取传入参数，这里实现你自己的动态规则
                String year = "_2018";
                int random = new Random().nextInt(10);
                if (random % 2 == 1) {
                    year = "_2019";
                }
                return tableName + year;
            });
        }});
        return dynamicTableNameParser;
    }

    /**
     * 多租户配置
     */

    private TenantSqlParser createTenantSqlParser(){
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId(boolean where) {
                // 该 where 条件 3.2.0 版本开始添加的，用于分区是否为在 where 条件中使用
                // 此判断用于支持返回多个租户 ID 场景，具体使用查看示例工程
                return new LongValue(1L);
            }

            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                // 这里可以判断是否过滤表
            /*
            if ("user".equals(tableName)) {
                return true;
            }*/
                return false;
            }
        });
        return tenantSqlParser;
    }
    /**
     * 乐观锁插件
     * 特别说明:
     *
     * 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
     * 整数类型下 newVersion = oldVersion + 1
     * newVersion 会回写到 entity 中
     * 仅支持 updateById(id) 与 update(entity, wrapper) 方法
     * 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
     *
     * @return
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {

        return new OptimisticLockerInterceptor();


    }


    @Bean(name = "dataSource")
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource dataSource() {
        return DruidDataSourceBuilder.create().build();
    }


    @DependsOn("dataSource")
    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager(
            @Qualifier(value = "dataSource") DataSource dataSource) {
        log.info("配置事务管理");
        return new DataSourceTransactionManager(dataSource);
    }

}
